<?php
require_once  'DangDangRequest.class.php';
/**
 *dangdang接口运行时发生错误时，抛出此异常.
 */
class DangDangRuntimeExecption extends Exception{
	function __construct($errorCode,$errorMessage){
		parent::__construct($errorMessage,$errorCode);
	}
}
/**
 * DangDang接口运行时.
 */
class DangDangRuntime {
	/**
	 * @var DangDangRequest $request 用于访问DangDang接口.
	 */
	var $request;
	/**
	 * @var integer $maxTryCount 调用可重调用数据不变接口函数失败最大重试次数，默认2次
	 */
	var $maxTryCount=2;
	/**
	 * @var integer $minTrySpanMicroseconds 调用可重调用数据不变接口函数失败最小重试时间间隔，毫秒数，默认300毫秒。
	 */
	var $minTrySpanMicroseconds=300;
	/**
	 * @var integer $maxTrySpanMicroseconds 调用可可重调用数据不变接口函数失败最大重试时间间隔，毫秒数，默认500毫秒。
	 */
	var $maxTrySpanMicroseconds=500;	
	
	/**
	 * 创建sina接口对象
	 * @var array $app_token 应用app访问dangdang的令牌，参数包括：<br/>
	 * string app_key :必须，后台设置的key值，将相关参数通过md5值<br/>
	 * string app_shopid :必须，商家在当当网开店的店铺编号。
	 */
	function __construct($app_token) { //
		if (!$app_token || ! is_array ( $app_token ))
			throw new DangDangRuntimeExecption ( - 1, 'DangDangRuntime创建失败：参数类型错误' );
		
		$app_key = $app_token ['app_key'];
		$app_shopid = $app_token ['app_shopid'];
		
		if (! ($app_key && $app_shopid ))
			throw new DangDangRuntimeExecption ( - 1, 'DangDangRuntime创建失败：参数错误' );
		
		$this->request = new DangDangRequest ();
        $this->request->app_token = array ('app_key' => $app_key,'app_shopid' => $app_shopid);		
	}
	function __destruct() {
	   $this->request=NULL;
	}
	/**
	 * 发布新商品，dangdang cmd: v2.addItems
	 * <br/>商家一次可以同时上传多个商品
	 * @param string $addItemsFileName 上传新商品XML文件，详见“上传新商品XML文件说明”及“上传新商品XML文件实例”
	 * @return 返回结果数组
	 */
	function addItems($addItemsFileName){
		if(!$addItemsFileName || !file_exists($addItemsFileName))
		  throw  new DangDangRuntimeExecption(-1,'addItems运行失败：参数错误或找不到文件');
		$paramarr=array();
		$request=clone $this->request;
		$request->httpmethod=DangDangRequest::HTTPMETHOD_POSTWITHFILE;
		$request->postWithFiles['addItems']=$addItemsFileName;		
		return $this->__fireRequest('v2.addItems',$paramarr,false,$request);
	}
	/**
	 * 修改商品详细信息，dangdang cmd: v2.updateItemsDetail
	 * <br/>商家一次可以同时修改多个商品，不允许修改“商品标识符”，其他值都允许被修改。
	 * 支持只修改部分信息；上传分色分码商品时，以最后一次信息为准
	 * @param string $addItemsFileName 修改商品XML文件，详见“XML文件说明”及“XML文件实例”
	 * @return 返回结果数组
	 */
	function updateItemsDetail($updateItemsDetailFileName){
		if(!$updateItemsDetailFileName || !file_exists($updateItemsDetailFileName))
		  throw  new DangDangRuntimeExecption(-1,'updateItemsDetail运行失败：参数错误或找不到文件');
		$paramarr=array();
		$request=clone $this->request;
		$request->httpmethod=DangDangRequest::HTTPMETHOD_POSTWITHFILE;
		$request->postWithFiles['updateItemsDetail']=$updateItemsDetailFileName;		
		return $this->__fireRequest('v2.updateItemsDetail',$paramarr,false,$request);
	}	
	/**
	 * 删除商品，dangdang cmd: v2.deleteItems
	 * <br/>商家提供商品标识符itemID，可以删除该商品；说明：如果该商品为赠品或上架状态，那么该商品不允许被删除；
	 * @param string $it 商品标识符，如果删除多个商品，以“_”隔开，例子：it=17375401_17375502
	 * @return 返回结果数组
	 */
	function deleteItems($it){
		if(!$it)  throw  new DangDangRuntimeExecption(-1,'deleteItems运行失败：参数错误');
		$paramarr=array('it'=>$it);
		return $this->__fireRequest('v2.deleteItems',$paramarr,false);
	}	
	/**
	 * 查询商品列表信息，dangdang cmd: v2.searchItems
	 * <br/>查询自己店铺的商品信息
	 * @param array $paramarr 查询参数，数组项：<br>
	 * <br> it 商品标识符          <br> oit 企业商品标识符     <br>name 商品名称
	 * <br> its 商品状态，共9种：9999：全部；9997：上架；0：可销售(预设值)；3：不可销售；9998：下架；6：商家下架；
	 * 1：举报下架；4：到期下架；5：强制下架。
	 * 说明：上架状态的商品含“可销售”和“不可销售”商品，下架状态商品含“商家下架”、“举报下架”、“到期下架”、“强制下架”的商品
	 * <br> sc 店内分类编号      <br>itb 品牌   <br>itm 型号/货号
	 * <br> mts 最后修改时间开始  <br> mte 最后修改时间结束
	 * <br> slv 库存数量最低值  <br> shv 库存数量最高值
	 * <br> p 页数，预设值为1
	 * @return 返回结果数组
	 */
	function searchItems(array $paramarr){
		return $this->__fireRequest('v2.searchItems',$paramarr);
	}		
	/**
	 * 查看商品详细信息，dangdang cmd: v2.getItemDetail
	 * <br/>查看单个商品的详细信息
	 * @param string $it 商品标识符
	 * @return 返回结果数组
	 */
	function getItemDetail($it){
		if(!$it)  throw  new DangDangRuntimeExecption(-1,'getItemDetail运行失败：参数错误');
		$paramarr=array('it'=>$it);
		return $this->__fireRequest('v2.getItemDetail',$paramarr);
	}	
	/**
	 * 更新商品库存，dangdang cmd: v2.updateItemStock
	 * @param string $oit 企业商品标识符
	 * @param integer $stk 库存
	 * @return 返回结果数组
	 */
	function updateItemStock($oit,$stk){
		if(!$oit || !isset($stk))  throw  new DangDangRuntimeExecption(-1,'updateItemStock运行失败：参数错误');
		$paramarr=array('oit'=>$oit,'stk'=>$stk);
		return $this->__fireRequest('v2.updateItemStock',$paramarr);
	}	
	/**
	 * 更新商品价格，dangdang cmd: v2.updateItemsInfo
	 * @param string $oit 企业商品标识符
	 * @param string $pr 库存
	 * @return 返回结果数组
	 */
	function updateItemsInfo($oit,$pr){
		if(!$oit || !isset($pr))  throw  new DangDangRuntimeExecption(-1,'updateItemsInfo运行失败：参数错误');
		$paramarr=array('oit'=>$oit,'pr'=>$pr);
		return $this->__fireRequest('v2.updateItemsInfo',$paramarr);
	}		
	/**
	 * 查询商品标识符，dangdang cmd: v2.getItemID
	 * @param string $oit 企业商品标识符
	 * @return 返回结果数组
	 */
	function getItemID($oit){
		if(!$oit)  throw  new DangDangRuntimeExecption(-1,'getItemID运行失败：参数错误');
		$paramarr=array('oit'=>$oit);
		return $this->__fireRequest('v2.getItemID',$paramarr);
	}	
	/**
	 * 查询企业商品标识符，dangdang cmd: v2.getOuterItemID
	 * @param string $it 商品标识符
	 * @return 返回结果数组
	 */
	function getOuterItemID($it){
		if(!$it)  throw  new DangDangRuntimeExecption(-1,'getOuterItemID运行失败：参数错误');
		$paramarr=array('it'=>$it);
		return $this->__fireRequest('v2.getOuterItemID',$paramarr);
	}	
		
	/**
	 * 查询订单列表信息，dangdang cmd: searchOrders
	 * <br/>商家可以查询自己店铺的订单列表信息
	 * @param array $paramarr 查询参数，数组项：<br>
	 * <br> o 订单编号         
	 * <br> os 订单状态，订单状态为以下其中一种：9999 全部;100  等待到款;
	 * 101  等待发货(预设值);
	 * 300  已发货;400  已送达;1000 交易成功;-100 取消;1100 交易失败。
	 * <br> sm 送货方式， 送货方式为以下其中一种：9999 全部(预设值)，1    普通快递，2    加急快递，3    普通邮递，4    邮政特快专递
	 * <br> pm 付款方式，付款方式为以下其中一种：9999 全部(预设值)；1    网上支付；2    银行汇款；3    邮局汇款。
	 * <br> name 收货人姓名 
	 * <br> osd 下单开始日期   ，格式：YYYY-MM-DD  可以查询出下单时间在“下单开始日期”当天及以后日期的商品
	 * <br> oed 下单开始日期  ，格式：YYYY-MM-DD  可以查询出下单时间在“下单结束日期”当天及以前日期的商品
	 * <br> sgsd 发货开始日期  ，格式：YYYY-MM-DD  可以查询出发货时间在“发货开始日期”当天及以后日期的商品
	 * <br> sged 发货结束日期  ，格式：YYYY-MM-DD  可以查询出发货时间在“发货结束日期”当天及以前日期的商品
	 * <br> ol  订单标记 ，商家自定义的订单标记
	 * <br> em  退换货处理标志 ，0:全部(预设值),1:需要进行退换货处理
	 * <br> p 页数，预设值为1
	 * @return 返回结果数组
	 */
	function searchOrders(array $paramarr){
		return $this->__fireRequest('searchOrders',$paramarr);
	}	
	/**
	 * 查看订单详细信息，dangdang cmd: getOrderDetail
	 * @param string $o 订单编号
	 * @return 返回结果数组
	 */
	function getOrderDetail($o){
		if(!$o)  throw  new DangDangRuntimeExecption(-1,'getOrderDetail运行失败：参数错误');
		$paramarr=array('o'=>$o);
		return $this->__fireRequest('getOrderDetail',$paramarr);
	}	
	/**
	 * 取消订单，dangdang cmd: cancelOrder
	 * <br>商家可以进行以下取消订单操作：取消订单状态为 “等待发货”的订单，商家一次只能取消一张订单
	 * @param string $o 订单编号
	 * @param string $cr 取消原因，只能填写数字：800003-800005 ， 800003 全部缺货，800004 无法送达，800005 顾客要求退款
	 * @return 返回结果数组
	 */
	function cancelOrder($o,$cr){
		if(!$o || !$cr)  throw  new DangDangRuntimeExecption(-1,'cancelOrder运行失败：参数错误');
		$paramarr=array('o'=>$o,'cr'=>$cr);
		return $this->__fireRequest('cancelOrder',$paramarr,false);
	}	
	/**
	 * 发货，dangdang cmd: sendGoods
	 * <br>商家可以将“等待发货”的订单修改为“已发货”，商家可以批量处理多张发货订单
	 * @param string $sendGoodsFileName 发货XML文件,详见“XML文件说明”及“XML文件实例”
	 * @return 返回结果数组
	 */
	function sendGoods($sendGoodsFileName){
		if(!$sendGoodsFileName || !file_exists($sendGoodsFileName))
		  throw  new DangDangRuntimeExecption(-1,'sendGoods运行失败：参数错误或找不到文件');		
		$paramarr=array();
		$request=clone $this->request;
		$request->httpmethod=DangDangRequest::HTTPMETHOD_POSTWITHFILE;
		$request->postWithFiles['sendGoods']=$sendGoodsFileName;		
		return $this->__fireRequest('sendGoods',$paramarr,false,$request);
	}	
	/**
	 * 退货处理，dangdang cmd: returnGoods
	 * <br>商家可以对用户的退货申请进行处理,商家一次只能处理一张订单
	 * @param string $o 订单编号
	 * @param string $ro 退货单编号
	 * @param string $mo 商家备注，长度：50个汉字内
	 * @param string $op 商家操作，1000、商家同意退款，1100、商家不同意退款
	 * @return 返回结果数组
	 */
	function returnGoods($o,$ro,$mo,$op){
		if(!$o || !$ro || !$mo || !$op)  throw  new DangDangRuntimeExecption(-1,'returnGoods运行失败：参数错误');
		$paramarr=array('o'=>$o,'ro'=>$ro,'mo'=>$mo,'op'=>$op);
		return $this->__fireRequest('returnGoods',$paramarr,false);
	}		
	/**
	 * 换货处理，dangdang cmd: exchangeGoods
	 * <br>商家可以对用户的换货申请进行处理，商家一次只能处理一张订单
	 * @param string $o  订单编号
	 * @param string $eo 换货单编号
	 * @param string $mo 商家备注，长度：50个汉字内
	 * @param string $op 商家操作，0:商家缺货退款，1:商家确认换货
	 * @return 返回结果数组
	 */
	function exchangeGoods($o,$eo,$mo,$op){
		if(!$o || !$eo || !$mo || !$op)  throw  new DangDangRuntimeExecption(-1,'exchangeGoods运行失败：参数错误');
		$paramarr=array('o'=>$o,'eo'=>$eo,'mo'=>$mo,'op'=>$op);
		return $this->__fireRequest('exchangeGoods',$paramarr,false);
	}		
	/**
	 * 上传商品图片，dangdang cmd: v2.setItemsPics
	 * <br>商家可以批量上传商品相关图片。每个商品最多上传3张图片；如果该商品在上传之前已经存在图片，
	 * 那么系统会将原图片全部删除，再发布新图片。若商品的销售属性含“颜色”，那么每种颜色最多上传1张图片；
	 * 如果某颜色在上传之前已经存在图片，那么系统会将原图片全部删除，再发布新图片。
	 * <br/>每张图片大小请限定在120k(建议800px*800px)以内，jpg或gif格式(不支持中文档名)
	 * @param string $setItemsPicsFileName rar文件，rar文件(最大20M)　包含：
	 * 1、XML文件：详见“XML文件说明”及“XML文件实例”，2、若干图片文件。
	 * @return 返回结果数组
	 */
	function setItemsPics($setItemsPicsFileName){
		if(!$setItemsPicsFileName || !file_exists($setItemsPicsFileName))
		  throw  new DangDangRuntimeExecption(-1,'setItemsPics运行失败：参数错误或找不到文件');		
		$paramarr=array();
		$request=clone $this->request;
		$request->httpmethod=DangDangRequest::HTTPMETHOD_POSTWITHFILE;
		$request->postWithFiles['setItemsPics']=$setItemsPicsFileName;		
		return $this->__fireRequest('v2.setItemsPics',$paramarr,false,$request);
	}		
	/**
	 * 查看近一个月上传失败的图片，dangdang cmd: v2.getItemsErrorPics
	 * <br/>商家可以查看当天0点之前一个月内，上传失败的图片错误信息；每天更新一次。
	 * @param string $it 商品标识符
	 * @return 返回结果数组
	 */
	function getItemsErrorPics(){
		return $this->__fireRequest('v2.getItemsErrorPics',array());
	}		
	/**
	 * 查询留言信息，dangdang cmd: searchQA
	 * @param array $paramarr 查询参数，数组项：<br>
	 * qsd 留言开始日期，格式：YYYY-MM-DD，可以查询出在“留言开始日期”当天及以后日期的“等待回复”留言
	 * qed 留言结束日期，格式：YYYY-MM-DD，可以查询出在“留言结束日期”当天及以前日期的“等待回复”留言
	 * p 页数,预设值为1
	 * @return 返回结果数组
	 */
	function searchQA(array $paramarr){
		if(!isset($paramarr))  throw  new DangDangRuntimeExecption(-1,'searchQA运行失败：参数错误');
		return $this->__fireRequest('searchQA',$paramarr);
	}	
	/**
	 * 回复留言，dangdang cmd: answerQA
	 * <br>商家可以：1、批量回答留言,2、修改留言的显示状态,3、删除留言
	 * @param string $answerQAFileName 回复留言XML文件,详见“XML文件说明”及“XML文件实例”
	 * @return 返回结果数组
	 */
	function answerQA($answerQAFileName){
		if(!$answerQAFileName || !file_exists($answerQAFileName))
		  throw  new DangDangRuntimeExecption(-1,'answerQA运行失败：发货XML文件');		
		$paramarr=array();
		$request=clone $this->request;
		$request->httpmethod=DangDangRequest::HTTPMETHOD_POSTWITHFILE;
		$request->postWithFiles['answerQA']=$answerQAFileName;		
		return $this->__fireRequest('answerQA',$paramarr,false,$request);
	}		
	/**
	 * 下载当当标准商品分类，dangdang cmd: v2.getDDCategory
	 * <br>商家可以下载自己经营分类下对应的当当标准商品分类
	 * @return 返回结果数组
	 */
	function getDDCategory(){
		return $this->__fireRequest('v2.getDDCategory',array());
	}	
	/**
	 * 下载当当销售属性值类，dangdang cmd: v2.getSpecialAttributeValue
	 * <br>商家可以下载自己经营分类下对应的当当标准商品分类
	 * @return 返回结果数组
	 */
	function getSpecialAttributeValue(){
		return $this->__fireRequest('v2.getSpecialAttributeValue',array());
	}		
	/**
	 * 下载当当标准品牌名称，dangdang cmd: v2.getDDBrand
	 * <br>商家可以下载商家经营标准品牌名称，如果有限制店铺的经营品牌，只返回限制的品牌列表
	 * @return 返回结果数组
	 */
	function getDDBrand(){
		return $this->__fireRequest('v2.getDDBrand',array());
	}	
	/**
	 * 下载店内分类，dangdang cmd: v2.getDDBrand
	 * <br>商家可以下载自定义的店内分类信息，商家需要到当当网商家后台自定义店内分类结构：
	 * <br>1、商家未设置店内分类结构：店铺首页的商品分类下，只显示“所有商品”
	 * <br>2、商家设置了店内分类结构：
	 * <br>分类编号：对于每个自定义分类，当当会提供对应的商家自定义分类编号；商家在发布和修改商品时，需要提供店内分类编号
	 * <br>最多两级分类，一级分类下可以有多个二级分类，二级分类下无其他子分类
	 * <br>首页默认分类：商家如果将某个一级分类或二级分类设为“首页默认”，那么打开店铺首页时，默认显示该分类的商品列表；
	 * <br>商家最多只能将一个分类设为“首页默认”；如果商家没有设置“首页默认”，那么店铺首页默认显示商家的全部商品
	 * @return 返回结果数组
	 */
	function getShopCategory(){
		return $this->__fireRequest('v2.getShopCategory',array());
	}	
	/**
	 * 查询促销列表信息，dangdang cmd: searchPromotions
	 * <br>商家可以查询“未开始”和“进行中”的促销活动信息
	 * @param string $prt  促销类型，9999：全部（预设值），1   ：商品多买多折扣，3   ：商品搭赠，12  ：订单满额赠
	 * @param string $prs  促销状态，9999：未开始和进行中（预设值），0   ：未开始，1   ：进行中
	 * @return 返回结果数组
	 */
	function searchPromotions($prt=9999,$prs=9999){
		if(!$prt || !$prs )  throw  new DangDangRuntimeExecption(-1,'searchPromotions运行失败：参数错误');
		$paramarr=array('prt'=>$prt,'prs'=>$prs);
		return $this->__fireRequest('searchPromotions',$paramarr);
	}
	/**
	 * 查看促销详细信息，dangdang cmd: getPromotion
	 * <br>商家可以查询“未开始”和“进行中”的促销活动信息
	 * @param string $pr  促销编号
	 * @return 返回结果数组
	 */
	function getPromotion($pr){
		if(!$pr )  throw  new DangDangRuntimeExecption(-1,'getPromotion运行失败：参数错误');
		$paramarr=array('pr'=>$pr);
		return $this->__fireRequest('getPromotion',$paramarr);
	}	
	/**
	 * 修改商品类型，dangdang cmd: updateItemType
	 * <br>商家可以将若干个商品修改为赠品，也可以将若干赠品修改为商品
	 * @param string $it  商品标识符，如果需要修改多个商品的类型，以“_”隔开
	 * @param integer $ty  商品类型，0、商品，1、赠品。
	 * @return 返回结果数组
	 */
	function updateItemType($it,$ty){
		if(!$it || !isset($ty) )  throw  new DangDangRuntimeExecption(-1,'updateItemType运行失败：参数错误');
		$paramarr=array('it'=>$it,'ty'=>$ty);
		return $this->__fireRequest('updateItemType',$paramarr);
	}	
	
	
	//----------------------private---------
	private function &__fireRequest($cmdid,array $paramArr,$retry=true,DangDangRequest $tmprequset=NULL){
		if($tmprequset) $request=$tmprequset;
		else  $request=$this->request;
		$tryCount=$this->maxTryCount;
		if ($retry) { //仅可对可 重调用数据不变接口函数 使用
			do {
				try {
					$arr = &$request->getArray ( $cmdid, $paramArr );
					if (! $arr || array_key_exists('Error',$arr)) 
						$tryCount --;
					else {
						$tryCount = - 1;
						break;
					}
				} catch ( Exception $e ) {
					$tryCount --;
				}
				if ($tryCount < $this->maxTryCount) {
					$ms = rand ( $this->minTrySpanMicroseconds, $this->maxTrySpanMicroseconds );
					usleep ( $ms );
				}
			} while ( $tryCount > 1 && $tryCount < $this->maxTryCount );
		}//仅可对可 重调用数据不变接口函数 使用
		if ($tryCount!=-1 || !$retry) {
			try {
				$arr = &$request->getArray ( $cmdid, $paramArr );
			} catch ( Exception $e ) {
				throw new DangDangRuntimeExecption (-2,'当当运行失败[-2]:' . $e->getMessage () );
			}
		}
		if(! $arr)
		    throw  new DangDangRuntimeExecption(-2,'当当运行失败[-2]:接口没有返回值，或接口返回xml格式错误解析失败');
		$errorCode=$errorMessage='';
		if(! $request->checkResult($arr,$errorCode,$errorMessage))
		     throw  new DangDangRuntimeExecption($errorCode,'当当运行失败['.$errorCode.']：'.$errorMessage);
		return $arr;
	}
}